/******************************************************************************
 *  Include Files
 *****************************************************************************/
#include "Bootloader.h"
#include "COM.h"
#include "DRV_MSCAN.h"
#include "NVM.h"
#include "DRV_FLASH.h"

/******************************************************************************
 *  Macro Definitions
 *****************************************************************************/
#define DATABUFMAX 40

/******************************************************************************
 *  Const Definitions
 *****************************************************************************/

/*****************************************************************************
 *  Global Variables Definitions
 *****************************************************************************/
static uint8_t Data_Buf[DATABUFMAX] = {0};
static uint8_t Data_Buf_cnt = 0;
static uint8_t Data_Buf_DataStartByte = 0;
static uint8_t Data_Buf_DataLen = 0;
static uint32_t Data_Buf_Addr = 0;

/*****************************************************************************
 *  Local Function declarations
 *****************************************************************************/
uint8_t Bootloader_Check_Data_Buf(void);

/*****************************************************************************
 *  Function Definitions
 *****************************************************************************/
void Bootloader_Main(void) {
  uint8_t FrameType = COM_BOOTLOADER_FRAMETYPE_OTHER;
  for (;;) {
    if (0 != DRV_MSCAN_IsRxFull()) {
      FrameType = COM_Read_Then_IsReq();
      if (COM_BOOTLOADER_FRAMETYPE_REQ == FrameType) {
        switch (COM_Req) {
        case COM_BOOTLOADER_SHAKEHANDS_REQ:
          COM_Send_Resp(COM_BOOTLOADER_SHAKEHANDS_RESP);
          break;
        case COM_BOOTLOADER_REBOOT_REQ:
          COM_Send_Resp(COM_BOOTLOADER_REBOOT_RESP);
          NVIC_SystemReset();
          break;
        case COM_BOOTLOADER_CHECKSUM_REQ:
          COM_Send_CheckSum();
          break;
        case COM_BOOTLOADER_FLASH_ERASE:
          DRV_FLASH_Erase(APP_START_ADDRESS,
                          CHECKSUM_ADDRESS + SECTOR_SIZE - APP_START_ADDRESS);
          DRV_FLASH_Main();
          COM_Send_Resp(COM_BOOTLOADER_RESP);
          break;
        case COM_BOOTLOADER_FLASH_PROG:
          if (1 == Bootloader_Check_Data_Buf()) {
            if ((Data_Buf_Addr >= APP_START_ADDRESS) &&
                ((Data_Buf_Addr + Data_Buf_DataLen) <=
                 (CHECKSUM_ADDRESS + SECTOR_SIZE))) {
              DRV_FLASH_Write(&Data_Buf[Data_Buf_DataStartByte], Data_Buf_Addr,
                              Data_Buf_DataLen);
              DRV_FLASH_Main();
            }
          }
          COM_Send_Resp(COM_BOOTLOADER_RESP);
          break;
        default:
          break;
        }
        Data_Buf_cnt = 0;
      } else if (COM_BOOTLOADER_FRAMETYPE_DATA == FrameType) {
        Data_Buf_cnt = COM_Data2Buf(Data_Buf, Data_Buf_cnt, DATABUFMAX);
        COM_Send_Resp(COM_BOOTLOADER_RESP);
      } else {
      }
    }
  }
}

uint8_t Bootloader_Check_Data_Buf(void) {
  uint8_t i = 0, chksum = 0;
  switch (Data_Buf[0]) {
  case 1:
    Data_Buf_DataStartByte = 4;
    Data_Buf_DataLen = Data_Buf[1] - 3;
    Data_Buf_Addr = (Data_Buf[2] << 8) | (Data_Buf[3]);
    break;
  case 2:
    Data_Buf_DataStartByte = 5;
    Data_Buf_DataLen = Data_Buf[1] - 4;
    Data_Buf_Addr = (Data_Buf[2] << 16) | (Data_Buf[3] << 8) | (Data_Buf[4]);
    break;
  case 3:
    Data_Buf_DataStartByte = 6;
    Data_Buf_DataLen = Data_Buf[1] - 5;
    Data_Buf_Addr = (Data_Buf[2] << 24) | (Data_Buf[3] << 16) |
                    (Data_Buf[4] << 8) | (Data_Buf[5]);
    break;
  default:
    return 0;
  }

  for (i = 0; i < Data_Buf[1]; i++) {
    chksum += Data_Buf[1 + i];
  }
  chksum = 0xFF - chksum;
  if (chksum == Data_Buf[Data_Buf[1] + 1]) {
    return 1;
  } else {
    return 0;
  }
}
